/*
 Copyright 2009-2010 AdMob, Inc.
 
    Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
  http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

package com.adwhirl;

/* ****************************************************************************
 * ScheduledExecutorService Leak patch / AdWhirlActivity callback hooks (rev.3)
 * 
 * Review Notes:
 * Changes submitted by Kent Butler based on the YourArcade.com build
 * of the Adwhirl 3.0 SDK. Modifications will be indicated by comments 
 * tagged with "KGB". Added public members provide basic JavaDoc
 * use/purpose explanations. 
 * 
 ******************************************************************************/

import com.adwhirl.adapters.AdWhirlAdapter;
import com.adwhirl.obj.Custom;
import com.adwhirl.obj.Extra;
import com.adwhirl.obj.Ration;
import com.adwhirl.util.AdWhirlUtil;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class AdWhirlLayout extends RelativeLayout {
  public static final String ADWHIRL_KEY = "ADWHIRL_KEY";
  public WeakReference<Activity> activityReference;

  // Only the UI thread can update the UI, so we need this for UI callbacks
  public final Handler handler = new Handler();

  // We also need a scheduler for background threads
  // KGB: Removed "final" so this can be killed and respawned (legacy mode trap).
  public ScheduledExecutorService scheduler = Executors .newScheduledThreadPool(1);

  private String keyAdWhirl;
  public Extra extra;

  // The current custom ad
  public Custom custom;

  // This is just so our threads can reference us explicitly
  public WeakReference<RelativeLayout> superViewReference;

  public Ration activeRation;
  public Ration nextRation;

  public AdWhirlInterface adWhirlInterface;

  public AdWhirlManager adWhirlManager;

  private boolean hasWindow;
  private boolean isScheduled;

  private int maxWidth;

  public void setMaxWidth(int width) {
    maxWidth = width;
  }

  private int maxHeight;

  public void setMaxHeight(int height) {
    maxHeight = height;
  }

  public AdWhirlLayout(final Activity context, final String keyAdWhirl) {
    super(context);
    init(context, keyAdWhirl);
  }

  public AdWhirlLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    // Retrieves AdWhirl key.
    String key = getAdWhirlKey(context);
    init((Activity) context, key);
    isStarting=true;
  }

  protected String getAdWhirlKey(Context context) {
    final String packageName = context.getPackageName();
    final String activityName = context.getClass().getName();
    final PackageManager pm = context.getPackageManager();
    Bundle bundle = null;
    // Attempts to retrieve Activity-specific AdWhirl key first. If not
    // found, retrieve Application-wide AdWhirl key.
    try {
      ActivityInfo activityInfo = pm.getActivityInfo(new ComponentName(
          packageName, activityName), PackageManager.GET_META_DATA);
      bundle = activityInfo.metaData;
      if (bundle != null) {
        return bundle.getString(AdWhirlLayout.ADWHIRL_KEY);
      }
    } catch (NameNotFoundException exception) {
      // Activity cannot be found. Shouldn't be here.
      return null;
    }

    try {
      ApplicationInfo appInfo = pm.getApplicationInfo(packageName,
          PackageManager.GET_META_DATA);
      bundle = appInfo.metaData;
      if (bundle != null) {
        return bundle.getString(AdWhirlLayout.ADWHIRL_KEY);
      }
    } catch (NameNotFoundException exception) {
      // Application cannot be found. Shouldn't be here.
      return null;
    }
    return null;
  }

  protected void init(final Activity context, final String keyAdWhirl) {
    this.activityReference = new WeakReference<Activity>(context);
    this.superViewReference = new WeakReference<RelativeLayout>(this);
    this.keyAdWhirl = keyAdWhirl;
    this.hasWindow = true;
    this.isScheduled = true;
    scheduler.schedule(new InitRunnable(this, keyAdWhirl), 0, TimeUnit.SECONDS);

    setHorizontalScrollBarEnabled(false);
    setVerticalScrollBarEnabled(false);

    this.maxWidth = 0;
    this.maxHeight = 0;
    
    if(!(context instanceof AdWhirlActivity) ){
        if(warnLegacy){

            Log.e(AdWhirlUtil.ADWHIRL,legacyStr);
        }
        legacyMode = true;
    }
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    if (maxWidth > 0 && widthSize > maxWidth) {
      widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth,
          MeasureSpec.AT_MOST);
    }

    if (maxHeight > 0 && heightSize > maxHeight) {
      heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight,
          MeasureSpec.AT_MOST);
    }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }

  /*
 // Original AdWhirl Function
  * 
  @Override
  protected void onWindowVisibilityChanged(int visibility) {
    if (visibility == VISIBLE) {
      this.hasWindow = true;
      if (!this.isScheduled) {
        this.isScheduled = true;

        if (this.extra != null) {
          rotateThreadedNow();
        } else {
          scheduler.schedule(new InitRunnable(this, keyAdWhirl), 0,
              TimeUnit.SECONDS);
        }
      }
    } else {
      this.hasWindow = false;
    }
  }
*/
  
  
  @Override
  /* ***Minimum Patch***
   * (return paths must be trapped)
   * 
   * KGB: Patched function addressing scheduler shutdown issue.
   * This core patch handles the scheduler shutdown function
   * when a AdWhirlLayout is destroyed. Accomplishes something
   * similar to the AdWhirlActivity event hook methodology.
   * 
   * Enabled as a fall-back for apps using adWhirl loaded through
   * a normal Android Activity.
   */ 
  protected void onWindowVisibilityChanged(int visibility) {
      if(!legacyMode)return;    //TODO: KGB - Run logic flow - maybe let this always execute as fallback? Seems unneeded overhead when hooking onDetatchedFromWindow() and no issues yet.
      if (visibility == VISIBLE) {
          this.hasWindow = true;
          if (!this.isScheduled || scheduler == null) { 
              if(scheduler == null){
                  scheduler = Executors.newScheduledThreadPool(1); // KGB: Create a new scheduler if one does not exist (should be created in init() if this was Destroyed).
                  if (consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"Visibility: scheduler created.");
              }
              this.isScheduled = true;
              if (this.extra != null) {
                  rotateThreadedNow();
              } else {
                  scheduler.schedule(new InitRunnable(this, keyAdWhirl), 0, TimeUnit.SECONDS);
              }
          }
      } else {
          this.hasWindow = false;
          isDestroyed = true;
          // Trap the case of a visibility call occurring with no scheduler (waving the device around).
          if(scheduler!=null){
              this.scheduler.shutdownNow();  // KGB: Shut down the threads so this scheduler doesn't persist if a new ad is created
              this.scheduler = null;         // KGB: Make sure the scheduler does not hold the view object
              if (consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"Visibility(hide): scheduler destroyed.");
          } else {
              if (consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"Visibility(hide): no scheduler.");
          }
      }
  }


  private void rotateAd() {
    if (!this.hasWindow) {
      if (consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"RotateAd() - AdWhirlLayout paused. Exiting.");
      this.isScheduled = false;
      return;
    }

    
    Log.i(AdWhirlUtil.ADWHIRL, "Rotating Ad");
    nextRation = adWhirlManager.getRation();

    handler.post(new HandleAdRunnable(this));
  }

  // Initialize the proper ad view from nextRation
  private void handleAd() {
    // We shouldn't ever get to a state where nextRation is null unless all
    // networks fail
    if (nextRation == null) {
      Log.e(AdWhirlUtil.ADWHIRL, "nextRation is null!");
      rotateThreadedDelayed();
      return;
    }

    String rationInfo = String
        .format(
            "Showing ad:\n\tnid: %s\n\tname: %s\n\ttype: %d\n\tkey: %s\n\tkey2: %s",
            nextRation.nid, nextRation.name, nextRation.type, nextRation.key,
            nextRation.key2);
    Log.d(AdWhirlUtil.ADWHIRL, rationInfo);

    try {
      AdWhirlAdapter.handle(this, nextRation);
    } catch (Throwable t) {
      Log.w(AdWhirlUtil.ADWHIRL, "Caught an exception in adapter:", t);
      rollover();
      return;
    }
  }

  // Rotate immediately
  public void rotateThreadedNow() {
      // KGB - Trap lingering threads in an old view.
      if(isDestroyed && consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"rotateThreadedNow() called after destruction. Exiting.");
      if( (!isDestroyed && scheduler==null)&& consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"rotateThreadedNow() called while not destroyed, but no scheduler? Exiting.");      
      if(isDestroyed || scheduler==null || !this.hasWindow){          
          this.isScheduled = false;
          return;
      }
      // --- KGB End ---
      scheduler.schedule(new RotateAdRunnable(this), 0, TimeUnit.SECONDS);
  }

  // Rotate in extra.cycleTime seconds
  public void rotateThreadedDelayed() {
      // KGB - Trap lingering threads in an old view.
      if(isDestroyed && consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"rotateThreadedNow() called after destruction. Exiting.");
      if( (!isDestroyed && scheduler==null)&& consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"rotateThreadedNow() called while not destroyed, but no scheduler? Exiting.");      
      if(isDestroyed || scheduler==null || !this.hasWindow){          
          this.isScheduled = false;
          return;
      }
      // --- KGB End ---
      
    Log.d(AdWhirlUtil.ADWHIRL, "Will call rotateAd() in " + extra.cycleTime
        + " seconds");
    scheduler.schedule(new RotateAdRunnable(this), extra.cycleTime,
        TimeUnit.SECONDS);
  }

  // Remove old views and push the new one
  public void pushSubView(ViewGroup subView) {
      // KGB - Trap lingering threads in an old view.      
      if(isDestroyed && consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"SubView pushed after destruction. Exiting.");
      if( (!isDestroyed && scheduler==null)&& consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"SubView Pushed while not destroyed, but no scheduler? Exiting.");
      if(isDestroyed || scheduler==null || !this.hasWindow){
          this.isScheduled = false;
          return;
      }
      // --- KGB End ---
    RelativeLayout superView = superViewReference.get();
    if (superView == null) {
      return;
    }
    superView.removeAllViews();

    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
        android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
        android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
    layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
    superView.addView(subView, layoutParams);

    Log.d(AdWhirlUtil.ADWHIRL, "Added subview");

    this.activeRation = nextRation;
    countImpression();
  }

  public void rollover() {
      // KGB - Trap lingering threads from an old view.
      if(isDestroyed && consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"Rollover called after destruction. Exiting.");
      if( (!isDestroyed && scheduler==null)&& consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"Rollover called while not destroyed, but no scheduler? Exiting.");
      if(isDestroyed || scheduler==null || !this.hasWindow){
          this.isScheduled = false;
          return;
      }
      // --- KGB End ---
    
    nextRation = adWhirlManager.getRollover();
    handler.post(new HandleAdRunnable(this));
  }

  private void countImpression() {
    // KGB: Trap for no window/scheduler/haswindow so we don't count the impression not visible ... shouldn't be possible to get here, but might as well.
    if (activeRation != null && scheduler != null && !isDestroyed  || !this.hasWindow) {
      String url = String.format(AdWhirlUtil.urlImpression,
          adWhirlManager.keyAdWhirl, activeRation.nid, activeRation.type,
          adWhirlManager.deviceIDHash, adWhirlManager.localeString,
          AdWhirlUtil.VERSION);
      scheduler.schedule(new PingUrlRunnable(url), 0, TimeUnit.SECONDS);
    }
  }

  private void countClick() {
    // KGB: Trap for no window/scheduler ... shouldn't be possible, but might as well.
    if (activeRation != null && scheduler != null && !isDestroyed) {
      String url = String.format(AdWhirlUtil.urlClick,
          adWhirlManager.keyAdWhirl, activeRation.nid, activeRation.type,
          adWhirlManager.deviceIDHash, adWhirlManager.localeString,
          AdWhirlUtil.VERSION);
      scheduler.schedule(new PingUrlRunnable(url), 0, TimeUnit.SECONDS);
    }
  }

  // We intercept clicks to provide raw metrics
  @Override
  public boolean onInterceptTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
      // Sending on an ACTION_DOWN isn't 100% correct... user could have touched
      // down and dragged out. Unlikely though.
      case MotionEvent.ACTION_DOWN:
        Log.d(AdWhirlUtil.ADWHIRL, "Intercepted ACTION_DOWN event");
        if (activeRation != null) {
          countClick();

          if (activeRation.type == 9) {
            if (custom != null && custom.link != null) {
              Intent intent = new Intent(Intent.ACTION_VIEW, Uri
                  .parse(custom.link));
              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
              try {
                if (activityReference == null) {
                  return false;
                }
                Activity activity = activityReference.get();
                if (activity == null) {
                  return false;
                }
                activity.startActivity(intent);
              } catch (Exception e) {
                Log.w(AdWhirlUtil.ADWHIRL, "Could not handle click to "
                    + custom.link, e);
              }
            } else {
              Log.w(AdWhirlUtil.ADWHIRL,
                      "In onInterceptTouchEvent(), but custom or custom.link is null");
            }
          }
          break;
        }
    }

    // Return false so subViews can process event normally.
    return false;
  }

  public interface AdWhirlInterface {
    public void adWhirlGeneric();
  }

  public void setAdWhirlInterface(AdWhirlInterface i) {
    this.adWhirlInterface = i;
  }

  private static class InitRunnable implements Runnable {
    private WeakReference<AdWhirlLayout> adWhirlLayoutReference;
    private String keyAdWhirl;

    public InitRunnable(AdWhirlLayout adWhirlLayout, String keyAdWhirl) {
      adWhirlLayoutReference = new WeakReference<AdWhirlLayout>(adWhirlLayout);
      this.keyAdWhirl = keyAdWhirl;
    }

    public void run() {
      AdWhirlLayout adWhirlLayout = adWhirlLayoutReference.get();
      if (adWhirlLayout != null) {
        Activity activity = adWhirlLayout.activityReference.get();
        if (activity == null) {
          return;
        }

        if (adWhirlLayout.adWhirlManager == null) {
          adWhirlLayout.adWhirlManager = new AdWhirlManager(
              new WeakReference<Context>(activity.getApplicationContext()),
              keyAdWhirl);
        }

        if (!adWhirlLayout.hasWindow) {
          adWhirlLayout.isScheduled = false;
          return;
        }

        adWhirlLayout.adWhirlManager.fetchConfig();
        adWhirlLayout.extra = adWhirlLayout.adWhirlManager.getExtra();

        if (adWhirlLayout.extra == null) {
          adWhirlLayout.scheduler.schedule(this, 30, TimeUnit.SECONDS);
        } else {
          adWhirlLayout.rotateAd();
        }
      }
    }
  }

  // Callback for external networks
  private static class HandleAdRunnable implements Runnable {
    private WeakReference<AdWhirlLayout> adWhirlLayoutReference;

    public HandleAdRunnable(AdWhirlLayout adWhirlLayout) {
      adWhirlLayoutReference = new WeakReference<AdWhirlLayout>(adWhirlLayout);
    }

    public void run() {
      AdWhirlLayout adWhirlLayout = adWhirlLayoutReference.get();
      if (adWhirlLayout != null) {
        adWhirlLayout.handleAd();
      }
    }
  }

  // Callback for pushing views from ad callbacks
  public static class ViewAdRunnable implements Runnable {
    private WeakReference<AdWhirlLayout> adWhirlLayoutReference;
    private ViewGroup nextView;

    public ViewAdRunnable(AdWhirlLayout adWhirlLayout, ViewGroup nextView) {
      adWhirlLayoutReference = new WeakReference<AdWhirlLayout>(adWhirlLayout);
      this.nextView = nextView;
    }

    public void run() {
      AdWhirlLayout adWhirlLayout = adWhirlLayoutReference.get();
      if (adWhirlLayout != null) {
        adWhirlLayout.pushSubView(nextView);
      }
    }
  }

  private static class RotateAdRunnable implements Runnable {
    private WeakReference<AdWhirlLayout> adWhirlLayoutReference;

    public RotateAdRunnable(AdWhirlLayout adWhirlLayout) {
      adWhirlLayoutReference = new WeakReference<AdWhirlLayout>(adWhirlLayout);
    }

    public void run() {
      AdWhirlLayout adWhirlLayout = adWhirlLayoutReference.get();
      if (adWhirlLayout != null) {
        adWhirlLayout.rotateAd();
      }
    }
  }

  private static class PingUrlRunnable implements Runnable {
    private String url;

    public PingUrlRunnable(String url) {
      this.url = url;
    }

    public void run() {
      Log.d(AdWhirlUtil.ADWHIRL, "Pinging URL: " + url);

      HttpClient httpClient = new DefaultHttpClient();
      HttpGet httpGet = new HttpGet(url);

      try {
        httpClient.execute(httpGet);
      } catch (ClientProtocolException e) {
        Log.e(AdWhirlUtil.ADWHIRL,
            "Caught ClientProtocolException in PingUrlRunnable", e);
      } catch (IOException e) {
        Log.e(AdWhirlUtil.ADWHIRL, "Caught IOException in PingUrlRunnable", e);
      }
    }
  }
  
  // ****************************************************************
  // **  KGB: Flags and routines to support scheduler termination. **
  // **  and AdWhirlActivity control functionality.                **
  // ****************************************************************

  // ------- View Override functions --------------
  @Override
  public void setVisibility (int visibility){
      if(consoleSpam)Log.w(AdWhirlUtil.ADWHIRL, "AdWhirlLayout - Setting visibility:"+visibility);
      if(legacyMode){
          onWindowVisibilityChanged(visibility);
          return;
      }
      if(visibility == VISIBLE){
          onResume();
      } else {
          onPause(false);
      }
      super.setVisibility(visibility);
  }
  
  @Override
  protected void onDetachedFromWindow(){
      String str="Null Scheduler";
      if(scheduler!=null){
          boolean b = scheduler.isShutdown();
          str = b ? "No":"Yes";
          onDestroy();
      }
      if(consoleSpam)Log.e(AdWhirlUtil.ADWHIRL,"Adwhirl layout detaching .... is Scheduler alive? "+str+" ***");
  }
  
  // -------- Activity event handling functions ------------------
  /**
   * Stub to accept event messages hooked by AdWhirlActivity. Allows AdWhirlLayout
   * objects to respond directly to pause/resume/destroy events.<BR><BR>
   * 
   * WARNING. Messages broadcast through this function are automatically generated
   * by the AdWhirlActivity in response to the corresponding events. Manual calls
   * should be made with caution. 
   * 
   * @param message One of the integer constant values indicating the message for this event. Defined in AdWhirlUtl.
   *                 must be one of:<BR>
   *                 &nbsp;&nbsp;&nbsp;<b>MSG_ONPAUSE</b> - Indicates and activity OnPause event initiated this message.<br>
   *                 &nbsp;&nbsp;&nbsp;<b>MSG_ONRESUME</b> - Indicates an activity OnResume event initiated this message.<br>
   *                  &nbsp;&nbsp;&nbsp;<b>MSG_ONDESTROY</b> - Indicates an activity OnResume event initiated this message.<br><br>
   * @param finishFlag Flag indicating if the event has been triggered by the Activity finishing ( equivalent to Activity isFinishing() ). 
   * @return <B>True</B> if there was a valid message and it was handled. <b>False</b> if the message type was unrecognized.
   * 
   * <br><br>(kgb)  
   */
  public boolean handleActivityMessage(int message, boolean finishFlag){
      if (consoleSpam) Log.w("System Event","Message Received by AdWhirlLayout!");
      String tmp=null;
      boolean rv = false;
      if(message == AdWhirlActivity.MSG_ONPAUSE){
          onPause(finishFlag);
          tmp="ONPAUSE";
          rv = true;
      } else if(message == AdWhirlActivity.MSG_ONRESUME){
          onResume();
          tmp="ONRESUME";
          rv = true;
      } else if(message == AdWhirlActivity.MSG_ONDESTROY){
          onDestroy(finishFlag);
          tmp="ONDESTROY";
          rv = true;
      } else {
          tmp="Unknown Message:"+message;
          rv = false;
      }
      if (consoleSpam) Log.w("System Event","Message processed:"+tmp);
      return rv;
  }
  
  protected void onPause(boolean isFinishing){      
      if(legacyMode)return;
      if (consoleSpam) Log.i(AdWhirlUtil.ADWHIRL,"System Event: AdWhirlLayout onPause("+isFinishing+")");      
      this.hasWindow = false; // Let everything settle down like normal.
  }
  
  protected void onResume(){      
      if(legacyMode)return;      
      if(isStarting==true){          
          isStarting=false;
          if (consoleSpam) Log.i(AdWhirlUtil.ADWHIRL,"System Event: AdWhirlLayout - just created onResume() ignored.");
          return;
      }     
      if (consoleSpam) Log.i(AdWhirlUtil.ADWHIRL,"System Event: AdWhirlLayout onResume()");
      this.hasWindow = true;
      if (!this.isScheduled) {
          this.isScheduled = true;
          if(scheduler == null){
              scheduler = Executors.newScheduledThreadPool(1); // KGB: Create a new scheduler if one does not exist (should be created on init onDestroy).
              if (consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"OnResume: scheduler created.");
          }
          if (this.extra != null) {
              rotateThreadedNow();
          } else {
              scheduler.schedule(new InitRunnable(this, keyAdWhirl), 0, TimeUnit.SECONDS);
          }
      }
  }
  
  protected void onDestroy(boolean isFinishing){
      if(legacyMode)return;      
      if (consoleSpam) Log.i("System Event","AdWhirlLayout onDestroy("+isFinishing+")");
      this.hasWindow = false;
      this.isDestroyed=true;
      if(scheduler!=null){
          this.scheduler.shutdownNow();  // KGB: Shut down the threads so this scheduler doesn't persist if a new ad is created
          this.scheduler = null;         // KGB: Make sure the scheduler does not hold the view object
          if (consoleSpam) Log.w(AdWhirlUtil.ADWHIRL,"OnDestroy: scheduler destroyed.");
      } else {
          if (consoleSpam) Log.i(AdWhirlUtil.ADWHIRL,"OnDestroy: no scheduler.");
      }

  }
  // ------------------ Explicit flow control ------------------------  
  /**
   * 
   * Report if the AdWhirlLayout is currently paused.<br><br>
   * 
   * The pause state is manually set using the Pause()
   * and unpause() methods. This state will also be
   * triggered automatically through callbacks from an
   * AdWhirlActivity in response to system events.
   * 
   * @return
   * True if the adWhirlLayout is paused. 
   * <br><br>(kgb)
   */
  public boolean isPaused()    { return !hasWindow; }
  
  /** 
   * Manually set AdWhirlLayout to the paused state.  
   * <br><br>(kgb) 
   */
  public void pause()
  {
      onPause(false);
  }
    
  /** 
   * Resume previously paused ad requests for the AdWhirlLayout.
   * <br><br>(kgb) 
   */
  public void unpause()
  {
      onResume();
  }
    
  // ---------------- Added field declarations -----------------------
  private boolean isDestroyed = false;
  private boolean isStarting = false;
  
  /** Spam the console? If this flag is true you will! */
  public static boolean consoleSpam = false;
  /** Flag to indicate if the adWhirlLayout was created by a non-AdWhirlActivity.<br><br>
   * 
   * If set to <b>True</b> the AdWhirlLayout will use the onWindowVisibilityChanged()
   * methodology instead of the onDestroy/onPause/onResume event methods that would
   * be called by an AdWhirlActivity.<br><br>
   * 
   * Activity type is detected by AdWhirlLayout objects on creation and this flag is set automatically.*/
  public boolean legacyMode = false;
  /** Error message to notify users when an AdWhirlLayout is operating in legacy mode */
  public static final String legacyStr = ""+
            "*********************************************************\n"+
            "* Warning - Adwhirl Layout being created by an Activity *\n"+
            "* that is not an AdWhirlActivity subclass!              *\n"+
            "*                                                       *\n"+
            "*              Switching to legacy mode .               *\n"+
            "*                                                       *\n"+
            "*  Ad rotation should be explicitly suspended/resumed   *\n"+
            "*  using the pause/unpause methods as reqired by the    *\n"+
            "*  Activity.                                            *\n"+
            "*********************************************************\n";
  /** Override to turn off console spam if intentionally running in legacy mode */
  public static boolean warnLegacy = true;

  // --- KGB END ---
}
    
